using System.Windows.Forms;
using NUnit.Framework;
using Rhino.Mocks;
using ShadeTree.Binding.Drivers;
using ShadeTree.Binding.Elements;
using ShadeTree.Binding.Settings;
using ShadeTree.Binding.Validation;
using ShadeTree.WinForms.Tests;

namespace ShadeTree.Binding.Tests.Elements
{
    [TestFixture]
    public class TextboxElementTester
    {
        #region Setup/Teardown

        [SetUp]
        public void SetUp()
        {
            LastChosenValues.Clear();
        }

        #endregion

        private void assertMaximumLengthOfProperty(string propertyName, int length)
        {
            Accessor accessor = SingleProperty.Build<TextboxTarget>(propertyName);
            var box = new TextBox();

            var element = new TextboxElement(accessor, box);
            box.MaxLength.ShouldEqual(length);
        }

        [Test]
        public void ApplyChangesWhenTheTextboxChanges()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Name);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            var target = new TextboxTarget();
            target.Name = "Jeremy";

            element.Bind(target);
            Assert.AreEqual(target.Name, box.Text);

            box.Text = "James";
            //TestUtility.FireEvent(box, "Validated");
            TestUtility.FireEvent(box, "Validated");
            Assert.AreEqual("James", target.Name);
        }

        [Test]
        public void BindAndResetThenApplyChanges()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Name);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            var target = new TextboxTarget();
            target.Name = "Jeremy";

            element.Bind(target);
            Assert.AreEqual(target.Name, box.Text);

            box.Text = "John";
            element.Reset();
            Assert.AreEqual("Jeremy", box.Text);

            box.Text = "John";
            element.ApplyChanges();
            Assert.AreEqual("John", target.Name);
        }

        [Test]
        public void BindAndResetThenApplyChangesWithANumber()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Age);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            var target = new TextboxTarget();
            target.Age = 33;

            element.Bind(target);
            Assert.AreEqual("33", box.Text);

            box.Text = 34.ToString();
            element.Reset();
            Assert.AreEqual("33", box.Text);

            box.Text = 35.ToString();
            element.ApplyChanges();
            Assert.AreEqual(35, target.Age);
        }


        [Test]
        public void BindAndSetValueToAnInvalidValueAndApplyDoNotFireHandlers()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Age);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);
            element.RegisterChangeHandler(delegate { Assert.Fail("The Change Handler should not be thrown"); });

            var target = new TextboxTarget();
            target.Name = "Jeremy";

            element.Bind(target);

            box.Text = "a";
        }

        [Test]
        public void BindAndSetValueToAnInvalidValueAndTryToApply()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Age);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            var target = new TextboxTarget();
            target.Name = "Jeremy";

            element.Bind(target);

            box.Text = "a";
            //TestUtility.FireEvent(box, "Validated");
            TestUtility.FireEvent(box, "LostFocus");
            //Assert.AreEqual(Notification.INVALID_FORMAT, element.GetError());
        }


        [Test]
        public void BindAndUpdateAndReset()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Name);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            var target = new TextboxTarget();
            target.Name = "Jeremy";

            element.Bind(target);
            Assert.AreEqual(target.Name, box.Text);

            target.Name = "James";
            element.Update();
            Assert.AreEqual("James", box.Text);

            element.Reset();
            Assert.AreEqual("Jeremy", box.Text);
            Assert.AreEqual("Jeremy", target.Name);
        }

        [Test, Ignore("Add Later")]
        public void ChangeTextClearValidationMessages()
        {
        //    Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Age);
        //    TextBox box = new TextBox();

        //    TextboxElement element = new TextboxElement(property, box);
        //    element.Provider = new ErrorProvider();

        //    TextboxTarget target = new TextboxTarget();
        //    target.Name = "Jeremy";

        //    element.Bind(target);
        //    element.SetError("something");

        //    box.Text = 5.ToString();
        //    //TestUtility.FireEvent(box, "Validated");
        //    TestUtility.FireEvent(box, "LostFocus");

        //    Assert.IsEmpty(element.GetError());
        }

        [Test, Ignore("Add Later")]
        public void ClearTheErrorWhenTheTextboxValueChangesSuccessfully()
        {
            //Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Name);
            //TextBox box = new TextBox();

            //TextboxElement element = new TextboxElement(property, box);
            //element.Provider = new ErrorProvider();

            //TextboxTarget target = new TextboxTarget();
            //target.Name = "Jeremy";

            //element.Bind(target);

            //element.SetError("something");

            //box.Text = "James";
            ////TestUtility.FireEvent(box, "Validated");
            //TestUtility.FireEvent(box, "LostFocus");
            //Assert.IsTrue(string.IsNullOrEmpty(element.GetError()));
        }

        [Test]
        public void FireTheChangeEventHandlersWhenTheTextboxValueChanges()
        {
            bool IWasFired = false;

            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Name);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            var target = new TextboxTarget();
            target.Name = "Jeremy";

            element.Bind(target);
            element.RegisterChangeHandler(( ) => IWasFired = true);
            box.Text = "James";

            TestUtility.FireEvent(box, "Validated");
            Assert.IsTrue(IWasFired);
        }


        [Test]
        public void GetABadNumber()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Age);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            var target = new TextboxTarget();
            target.Age = 33;

            element.Bind(target);

            box.Text = "abc";
        }

        [Test]
        public void LostFocusFiresHandler()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Age);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);
            bool iWasCalled = false;
            element.RegisterLostFocusHandler(( ) => iWasCalled = true);
            TestUtility.FireEvent(box, "LostFocus");

            iWasCalled.ShouldBeTrue();
        }

        [Test]
        public void LostFocusTellsTheParentScreenBinderToValidate()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Age);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            var mocks = new MockRepository();
            using (mocks.Record())
            {
                var binder = mocks.StrictMock<IScreenBinder>();
                element.Binder = binder;
                Expect.Call(binder.IsLatched).Return(false).Repeat.Any();

                binder.Validate(element);
            }

            using (mocks.Playback())
            {
                TestUtility.FireEvent(box, "LostFocus");
            }
        }


        [Test]
        public void RetrieveLastChosenValueOnBinding()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Name);
            var box = new TextBox();
            var panel = new Panel();
            panel.Controls.Add(box);

            LastChosenValues.Store(ControlDriverFactory.GetDriver(box), "LastValue");

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);
            element.RememberLastChoice();
            element.Bind(new TextboxTarget());

            box.Text.ShouldBeEmpty();

            bool iWasCalled = false;
            element.RegisterChangeHandler(( ) => iWasCalled = true);

            element.SetDefaults();

            iWasCalled.ShouldBeTrue();
            LastChosenValues.Retrieve(box).ShouldEqual("LastValue");
        }

        [Test]
        public void SetTheMaximumTextLength()
        {
            assertMaximumLengthOfProperty("Name", 10);
            assertMaximumLengthOfProperty("LastName", 100);
        }

        [Test]
        public void StoreValuesOnChange()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Name);
            var box = new TextBox();
            var panel = new Panel();
            panel.Controls.Add(box);

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);
            element.RememberLastChoice();
            element.Bind(new TextboxTarget());

            box.Text.ShouldBeEmpty();
            LastChosenValues.Retrieve(box).ShouldBeNull();

            var handler = new TextBoxHandler();
            handler.Enter(box, "NewValue");

            LastChosenValues.Retrieve(box).ShouldEqual("NewValue");
        }

        [Test]
        public void TextBoxBindsToADoubleAlignmentShouldBeRight()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Age);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            Assert.AreEqual(HorizontalAlignment.Right, box.TextAlign);
        }

        [Test]
        public void TextBoxBindsToAStringAlignmentShouldBeLeft()
        {
            Accessor property = SingleProperty.Build<TextboxTarget>(x => x.Name);
            var box = new TextBox();

            BoundScreenElement<TextBox, object> element = new TextboxElement(property, box);

            Assert.AreEqual(HorizontalAlignment.Left, box.TextAlign);
        }
    }

    internal class TextboxTarget
    {
        public double Amount { get; set; }

        public int Age { get; set; }

        [MaximumStringLength(10)]
        public string Name { get; set; }


        public string LastName { get; set; }
    }
}